#pragma once

#include <semaphore.h>
#include <vector>//使用数组来模拟环形队列

template <class T,size_t N = 5>//默认队列大小为5
class ring_queue
{
public:
    ring_queue()
        :_con(N),_pindex(0),_cindex(0)
    {
        //一开始队列是空的
        sem_init(&_psem,0,N);
        sem_init(&_csem,0,0);
        pthread_mutex_init(&_pmtx,nullptr);
        pthread_mutex_init(&_cmtx,nullptr);
    }

    //为生产者提供push方法
    void push(const T& data)
    {
        /*所不要放在P操作之前，先让生产者获得该资源。具体生不生产再由竞争锁的能力决定*/
        //首先进行P操作
        P(_psem);

        pthread_mutex_lock(&_pmtx);//生产者与生产者之间保持互斥
        //生产数据
        _con[_pindex++] = data;
        _pindex %= N;
        pthread_mutex_unlock(&_pmtx);

        V(_csem);//离开临界区 -> V操作
    }

    //为消费者提供pop方法
    void pop(T* data)
    {
        P(_csem);

        pthread_mutex_lock(&_cmtx);
        *data = _con[_cindex++];
        _cindex %= N;
        pthread_mutex_unlock(&_cmtx);

        V(_psem);//消费一个，空位多一个
    }

    ~ring_queue()
    {
        sem_destroy(&_psem);
        sem_destroy(&_csem);
        pthread_mutex_destroy(&_pmtx);
        pthread_mutex_destroy(&_cmtx);
    }
private:
    std::vector<T> _con;
    sem_t _psem;//生产者信号量 -> 看重队列当中的空位
    sem_t _csem;//消费者信号量 -> 看重队列当中的数据
    size_t _pindex;//生产者队列下标
    size_t _cindex;//消费者队列下标
    pthread_mutex_t _pmtx;
    pthread_mutex_t _cmtx;

    void P(sem_t &sem)
    {
        int n = sem_wait(&sem);
        if(n < 0)
        {
            cerr << "sem_wait error" << strerror(errno) << endl;
        }
    }

    void V(sem_t& sem)
    {
        int n = sem_post(&sem);
        if(n < 0)
        {
            cerr << "sem_post error" << strerror(errno) << endl;
        }
    }
};